#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/parsers.hpp>

#include <stdio.h>
#include <string.h>

#include "common/global_init.h"
#include "common/global_context.h"
#include "bluestore/BlueStore.h"
#include "common/ceph_argparse.h"
#include "common/errno.h"
#include "common/osd_types.h"
#include "common/Cond.h"
#include "include/uuid.h"

using namespace std;
namespace po = boost::program_options;

#define dout_context g_ceph_context

void usage(po::options_description &desc)
{
	cerr << "must specify an action(mkfs, mount or umount) and the bluestore path; " << std::endl;
}

int write_meta(CephContext *cct, BlueStore *store, uuid_d& cluster_fsid, uuid_d& osd_fsid)
{
	char val[80];
	int r;

  	snprintf(val, sizeof(val), "%s", CEPH_OSD_ONDISK_MAGIC);
  	r = store->write_meta("magic", val);
	if (r < 0)
		return r;
  
	cluster_fsid.print(val);
	r = store->write_meta("ceph_fsid", val);
	if (r < 0)
		return r;

	r = store->write_meta("ready", "ready");
	if (r < 0)
		return r;
	return 0;
}

int main(int argc, char **argv)
{
	string path;
	string action;
	// set the options
	po::options_description po_options("Options");


	po_options.add_options()
		("help,h", "produce help message")
		("path", po::value<string>(&path), "bluestore path")
	;
	po::options_description po_positional("Positional options");
	po_positional.add_options()
		("command", po::value<string>(&action),
        	"mkfs, "
        	"mount, "
        	"umount, ")
    	;

        po::options_description po_all("All options");
	po_all.add(po_options).add(po_positional);
  	po::positional_options_description pd;
	pd.add("command", 1);

	// parse options
	po::variables_map vm;
	try {
		po::parsed_options parsed =
		po::command_line_parser(argc, argv).options(po_all).allow_unregistered().positional(pd).run();

		po::store(parsed, vm);
		po::notify(vm);
	} catch(po::error &e) {
		std::cerr << e.what() << std::endl;
		exit(EXIT_FAILURE);
	}

	if (vm.count("help")) {
		usage(po_all);
		exit(EXIT_FAILURE);
	}
	if (action.empty()) {
		cerr << "must specify an action(mkfs, mount or umount) and the bluestore path; " << std::endl;
		exit(EXIT_FAILURE);
	}

	int ret = 0;
	OSDSuperblock sb;
	bufferlist sbbl;
	BlueStore::Sequencer *osr = new BlueStore::Sequencer("mkfs");
	C_SaferCond waiter;
	vector<const char*> args;
	argv_to_vec(argc, (const char **)argv, args);
	env_to_vec(args);

	auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_OSD, CODE_ENVIRONMENT_DAEMON, CINIT_FLAG_UNPRIVILEGED_DAEMON_DEFAULTS);
	common_init_finish(cct.get());

	// do the command
	if (action == "mkfs") {
		if (path.empty()) {
			cerr << "must specify the bluestore path" << std::endl;
			exit(EXIT_FAILURE);
		}
		BlueStore *store = new BlueStore(cct.get(), path); 
		store->set_fsid(cct->_conf->osd_uuid);
		ret = store->mkfs();
		if (ret) {
			cerr << "mkfs error: " << cpp_strerror(ret) << std::endl;
			exit(EXIT_FAILURE);
		}
        	cout << action << " successfully." << std::endl;
		ret = store->mount();
		if (ret) {
			cerr << "mount error: " << cpp_strerror(ret) << std::endl;
			exit(EXIT_FAILURE);
		}
        	cout << "mount successfully." << std::endl;

		ret = store->read(coll_t::meta(), OSD_SUPERBLOCK_GOBJECT, 0, 0, sbbl);
		if (ret >= 0) {
			cout << "has already have superblock." << std::endl;
			bufferlist::iterator p;
			p = sbbl.begin();
			::decode(sb, p);
			if (cct->_conf->osd_uuid != sb.cluster_fsid) {
				cerr << "provided cluster fsid " << cct->_conf->osd_uuid << " != superblock " << sb.cluster_fsid << std::endl;
				exit(EXIT_FAILURE);
			}
		} else {
			// create osd superblock
			sb.cluster_fsid = cct->_conf->osd_uuid;
    			sb.osd_fsid = store->get_fsid();

    			bufferlist bl;
    			::encode(sb, bl);

    			ObjectStore::Transaction t;
    			t.create_collection(coll_t::meta(), 0);
    			t.write(coll_t::meta(), OSD_SUPERBLOCK_GOBJECT, 0, bl.length(), bl);
    			ret = store->apply_transaction(osr, std::move(t));
    			if (ret) {
      				cerr << "mkfs error while writing OSD_SUPERBLOCK_GOBJECT: "
	   			<< "apply_transaction returned " << cpp_strerror(ret) << std::endl;
    			}
		}
		if (!osr->flush_commit(&waiter)) {
			waiter.wait();
		}
		ret = write_meta(cct.get(), store, sb.cluster_fsid, sb.osd_fsid);
		if (ret) {
    			cerr << "mkfs failed to write fsid file: error " << cpp_strerror(ret) << std::endl;
			exit(EXIT_FAILURE);
		}
        	cout << "write meta successfully." << std::endl;
	}
	else if (action == "mount") {
		BlueStore *store = new BlueStore(cct.get(), path); 
		ret = store->mount();
		if (ret) {
			cerr << "mount error: " << cpp_strerror(ret) << std::endl;
			exit(EXIT_FAILURE);
		}
        	cout << action << " successfully." << std::endl;
	}
	else if (action == "umount") {
		BlueStore *store = new BlueStore(cct.get(), path); 
		ret = store->umount();
		if (ret) {
			cerr << "umount error: " << cpp_strerror(ret) << std::endl;
			exit(EXIT_FAILURE);
		}
        	cout << action << " successfully." << std::endl;
	}
	else {
		cerr << "must specify an action(mkfs, mount or umount) and the bluestore path; " << std::endl;
		exit(EXIT_FAILURE);
	} 
	return ret;
}
